package es.battleship.service;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import es.battleship.api.dao.PlayerPairDao;
import es.battleship.api.service.ConfigService;
import es.battleship.api.service.GameService;
import es.battleship.model.Cell;
import es.battleship.model.Game;
import es.battleship.model.GameConfig;
import es.battleship.model.GameStatus;
import es.battleship.model.Board;
import es.battleship.model.PlayerPair;
import es.battleship.model.Ship;
import es.battleship.model.ShipConfig;
import es.battleship.model.Shot;
import es.battleship.resource.GameResource;

@Service
public class GameServiceImpl implements GameService {
	Logger  logger = Logger.getLogger(GameResource.class.getName());
	
	private PlayerPairDao playerPairDao;
	private ConfigService configService;
	
	@Autowired
	public void setPlayerPairDao(PlayerPairDao playerPairDao) {
		this.playerPairDao = playerPairDao;
	}
	
	@Autowired
	public void setConfigService(ConfigService configService) {
		this.configService = configService;
	}


	@Override
	public String invite(String user1, String user2) throws Exception {

		PlayerPair playerPair = new PlayerPair();
		playerPair.setBoard1(new Board());
		playerPair.setUser1(user1);
		playerPair.setBoard2(new Board());
		playerPair.setUser2(user2);
		
		GameConfig gameConfig = buildDefaultGameConfig();
		
		Game game = new Game();
		game.setStatus(GameStatus.StartUp);
		game.setGameConfig(gameConfig);

		playerPair.setGame(game);

		PlayerPair p = playerPairDao.create(playerPair);
		
		return Long.toString(p.getId().getId());

	}

	@Override
	@Transactional
	public String accept(Long gameId,String userId2) throws Exception {

		PlayerPair  playerPair = playerPairDao.find(gameId);
		
		logger.info("playerPair " + playerPair);
		
		
		String userId1 = playerPair.getUser1();
		
		logger.info("User1 "+ userId1);
		
		String sotreId2 = playerPair.getUser2();
		
		logger.info("User2 "+ sotreId2);
		
		if ( userId2.equals( sotreId2 ) ) {
			logger.info("accept " + gameId);

			playerPair.getGame().setStatus(GameStatus.Joined);
			playerPairDao.create(playerPair);
			
			return userId1;
			
		} else {
			throw new Exception();
		}
	}
	
	@Override
	@Transactional
	public List<PlayerPair> getAllFromUser(String userId) {
		List<PlayerPair>  playerPairs = playerPairDao.findByUser(userId);
 
		return playerPairs;
	}


	@Override
	public void addShip(Long gameId, String userId, List<Ship> ships) throws Exception {
		logger.info("Add Ship " + userId + " "  + ships);
		
		PlayerPair  playerPair = playerPairDao.find(gameId);
		
		Board board = foundBoard(playerPair,userId);
		logger.info("Board found " + board);
		
		Game game = playerPair.getGame();

		if (  game.getStatus() == GameStatus.Configuring )  {
			
			if (configService.isComplete(game.getGameConfig(),board) ) {
				logger.info("Configuration complete, not possible to add more ships.");
				
				throw new Exception("Configuration complete, not possible to add more ships.");
			}

			
			for (Ship ship : ships) {
				configService.isValid (game, board,ship); 
				board.getShips().add(ship);
				for (Cell cell : ship.getCells()) {
					cell.setHit(false);
					cell.setShip(ship);
				}
			}
			
			if (!configService.isComplete(game.getGameConfig(),board) ) {
				logger.info("Ship configuration incorrect.");
				
				throw new Exception("Ship configuration incorrect.");
			}
			
			if (configService.isComplete(game.getGameConfig(),playerPair.getBoard1()) && 
					configService.isComplete(game.getGameConfig(),playerPair.getBoard2())) {
				game.setStatus(GameStatus.Round1);
			}

			playerPairDao.create(playerPair);
					
		} else {
			logger.info("Wrong State");
			
			throw new Exception("Wrong State");
		}

		// TODO Auto-generated method stub
		
	}

	@Override
	public Boolean shot(Long gameId, String userId, Shot shot) throws Exception {
		logger.info("shot " + userId + " " + shot);
		
		PlayerPair  playerPair = playerPairDao.find(gameId);
		
		Board player = foundBoard(playerPair,userId);
		logger.info("Player found " + player);
		
		Game game = playerPair.getGame();
		
		
		Boolean isHit = false;		
		switch (game.getStatus()) {
			case Round1:
				logger.info("Round 1 ");
				
					  
				isHit = shot(playerPair.getBoard1(),shot);
				
				logger.info("isHit "+ isHit);
				
				if (isAllShipDestroyed(playerPair.getBoard2()) ) {  
					game.setStatus(GameStatus.Wind1);
					
					logger.info("All Ship Destroyed ");
					
				} else 
					game.setStatus(GameStatus.Round2);
					
				break;
			case Round2:
				logger.info("Round 2 ");

				isHit = shot(playerPair.getBoard1(),shot);
				
				logger.info("isHit "+ isHit);
				
				if (isAllShipDestroyed(playerPair.getBoard1()) ) {  
					game.setStatus(GameStatus.Wind2);
					
					logger.info("All Ship Destroyed ");
					
				} else
					game.setStatus(GameStatus.Round1);
				break;
			case Wind1:
				logger.info("Wind 1 ");
					
				isHit = shot(playerPair.getBoard1(),shot);
				
				logger.info("isHit "+ isHit);
				
				if (isAllShipDestroyed(playerPair.getBoard1()) )  {
					game.setStatus(GameStatus.Draw);
					
					logger.info("All Ship Destroyed ");
				} else {
					game.setStatus(GameStatus.Wind1);
				}
					
				break;
				
			default:
				throw new Exception("Wrong state");
		}
		
		playerPairDao.create(playerPair);
		
		return isHit;
	}

	
	private GameConfig buildDefaultGameConfig() {
		List<ShipConfig> shipConfigs = new ArrayList<ShipConfig>();
		
		ShipConfig shipConfig1 = new ShipConfig();
		shipConfig1.setName("aircraft carrier");
		shipConfig1.setNumber(1);
		shipConfig1.setSize(5);
		shipConfigs.add(shipConfig1);
		
		ShipConfig shipConfig2 = new ShipConfig();
		shipConfig2.setName("battleship");
		shipConfig2.setNumber(1);
		shipConfig2.setSize(4);
		shipConfigs.add(shipConfig2);

		ShipConfig shipConfig3 = new ShipConfig();
		shipConfig3.setName("cruiser");
		shipConfig3.setNumber(1);
		shipConfig3.setSize(3);
		shipConfigs.add(shipConfig3);
		
		ShipConfig shipConfig4 = new ShipConfig();
		shipConfig4.setName("destroyer");
		shipConfig4.setNumber(2);
		shipConfig4.setSize(2);
		shipConfigs.add(shipConfig4);
		
		ShipConfig shipConfig5 = new ShipConfig();
		shipConfig5.setName("submarine");
		shipConfig5.setNumber(2);
		shipConfig5.setSize(1);
		shipConfigs.add(shipConfig5);
		
		GameConfig gameConfig = new GameConfig();
		gameConfig.setDimensionX(10);
		gameConfig.setDimensionY(10);
		gameConfig.setShipConfigs(shipConfigs);
		
		return gameConfig;
	}
	
	private Board foundBoard(PlayerPair playerPair,String userId) throws Exception {
		logger.info("found Player " + playerPair + " " + userId);
		
		if (playerPair.getUser1().equals(userId)) {
			return playerPair.getBoard1();
		} else if (playerPair.getUser2().equals(userId)) {
			return playerPair.getBoard2();
		} else {
			throw new Exception("Player not found");
		}   
	}
	
	public Boolean shot(Board player,Shot shot) {
		for (Ship ship : player.getShips() )
			for (Cell cell: ship.getCells() ) {
				if (cell.getX()==shot.getX() && cell.getY()==shot.getY()) {
					cell.setHit(true);
					return true;
				}
			}
		return false;
	}

	public Boolean isAllShipDestroyed(Board player)  {
		for (Ship ship : player.getShips() )
			for (Cell cell: ship.getCells() ) {
				if (!cell.getHit()) {
					return false;
				}
			}
		return true;
	}


}
